package com.gitee.chili.leisure.util;

import io.netty.channel.Channel;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.util.concurrent.GlobalEventExecutor;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;

import java.util.List;

/**
 * netty 信道连接管理器
 *
 * @author week solider
 * @since 2020.09.07
 */
public final class Connection {

    /**
     * 缓存持有者实例
     */
    public static final ChannelHolder HOLDER = new ChannelHolder();


    private Connection() {
    }

    /**
     * 获取广播器
     */
    public static ChannelHolder broadcaster() {
        return HOLDER;
    }

    /**
     * 信道持有者
     */
    public static class ChannelHolder {


        /**
         * 所有玩家的信道存储器
         */
        private final ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

        /**
         * 房间内玩家信道组
         */
        private final MultiValueMap<Long, MemberChannel> channelMap = new LinkedMultiValueMap<>(256);


        private ChannelHolder() {
        }

        /**
         * 添加信道
         */
        public void set(Long roomNum, MemberChannel channel) {
            channelMap.add(roomNum, channel);
        }

        /**
         * 从房间信道缓存中移除
         *
         * @param roomNum  房间号
         * @param memberId 玩家id
         */
        public void exit(Long roomNum, Long memberId) {
            channelMap.get(roomNum).removeIf(channel -> channel.getMemberId().equals(memberId));
        }

        /**
         * 移除房间信道信息
         *
         * @param rooNum 房间号
         */
        public void remove(Long rooNum) {
            channelMap.remove(rooNum);
        }

        /**
         * 获取信道
         */
        public List<MemberChannel> obtain(Long roomNum) {
            return channelMap.get(roomNum);
        }

        /**
         * 推送消息给房间内的玩家
         *
         * @param roomNum     房间号
         * @param memberId    玩家ID
         * @param msg         广播消息
         * @param excludeSelf 是否发送给自己
         */
        private void notify(Long roomNum, Long memberId, Object msg, boolean excludeSelf) {

            for (MemberChannel mc : obtain(roomNum)) {

                if (excludeSelf && mc.getMemberId().equals(memberId)) {
                    continue;
                }
                mc.getChannel().writeAndFlush(msg);
            }
        }

        /**
         * 推送消息 自己不接收
         * {@link #notify(Long, Long, Object, boolean)}
         */
        public void notify(Long roomNum, Long memberId, Object msg) {
            notify(roomNum, memberId, msg, true);
        }

        /**
         * 推送消息 自己也接收
         * {@link #notify(Long, Long, Object, boolean)}
         */
        public void notify(Long roomNum, Object msg) {
            notify(roomNum, null, msg, false);
        }


        /**
         * 推送消息给所有游戏中(包含游戏未开始)的玩家
         *
         * @param msg 需要推送的消息
         */
        public void notify(Object msg) {
            MultiValueMap<Long, MemberChannel> map = channelMap;
            map.forEach((k, v) -> map.get(k).forEach(c -> c.getChannel().writeAndFlush(msg)));
        }

        /**
         * 私密消息，只有玩家自己可见
         *
         * @param roomNum  房间号
         * @param memberId 玩家ID
         * @param msg      消息
         */
        public void send(Long roomNum, Long memberId, Object msg) {
            obtain(roomNum).stream()
                    .filter(mc -> mc.getMemberId().equals(memberId)).findFirst()
                    .ifPresent(channel -> channel.getChannel().writeAndFlush(msg));
        }

        /**
         * 广播给所有在线玩家
         *
         * @param msg 消息体
         */
        public void broadcast(Object msg) {
            channelGroup.writeAndFlush(msg);
        }

        /**
         * 添加到信道组
         */
        public void add(Channel channel) {
            channelGroup.add(channel);
        }

        /**
         * 从信道组中移除
         */
        public void remove(Channel channel) {
            channelGroup.remove(channel);
        }
    }
}
